/** \file
* Declaration of the debug console class.
* This file is part of Chorus, available at
* http://code.google.com/p/chorus-twitter/ . This file and all associated files
* and derivative works are bound by the terms of the LGPL 3.0 license,
* available at http://www.gnu.org/licenses/lgpl-3.0-standalone.html .
* \par Subversion URL:
* \$URL: http://chorus-twitter.googlecode.com/svn/branches/cpp/src/system/DebugConsole.hpp $
* \version \$Rev: 152 $
* \date    \$Date: 2010-04-06 07:35:13 +0000 (Tue, 06 Apr 2010) $
*/

#pragma once

#include <ostream>
#include <sstream>
#include <list>

#include <curl/curl.h>

#include "system/Mutex.hpp"

#ifdef USING_DEBUGCONSOLE

/// The debug console class. Outputs to streams with mutex control. The print*
/// methods are meant to be called by the macros below the class definition;
/// these automatically supply the file, function and line arguments.
class DebugConsole
{
// -------------------------- Member functions --------------------------------

public:
	/// Add a stream to the output list.
	/// \param stream A pointer to the stream to add.
	void addStream(std::ostream *stream);

	/// Print a generic warning, error, etc to the streams. PrintFunctor is the
	/// functor to use for specialized printing. This locks the mutex to
	/// prevent stream badness.
	/// \param file     The source file name.
	/// \param function The function name.
	/// \param line     The source file line.
	/// \param message  The message to print.
	template<typename PrintFunctor>
	void printDebug1(const char *file, const char *function, int line,
		const char *message)
	{
		streammutex.lock();
		printDebug1_unguarded<PrintFunctor>(file, function, line, message);
		streammutex.unlock();
	}

	/// Print a generic warning, error, etc to the streams. PrintFunctor is the
	/// functor to use for specialized printing. This does not lock the mutex;
	/// use only in emergencies!
	/// \param file     The source file name.
	/// \param function The function name.
	/// \param line     The source file line.
	/// \param message  The message to print.
	template<typename PrintFunctor>
	void printDebug1_unguarded(const char *file, const char *function,
		int line, const char *message)
	{
		printStandard(file, function, line, message);
		PrintFunctor::print(buffer);
		flush();
	}

	/// Print a generic warning, error, etc to the streams, plus another
	/// message of arbitrary type. PrintFunctor is the functor to use for
	/// specialized printing. This locks the mutex to prevent stream badness.
	/// \param file     The source file name.
	/// \param function The function name.
	/// \param line     The source file line.
	/// \param message1 The message to print.
	/// \param message2 The second message to print.
	template<typename PrintFunctor, typename Msg2Type>
	void printDebug2(const char *file, const char *function, int line,
		const char *message1, Msg2Type message2)
	{
		streammutex.lock();
		printDebug2_unguarded<PrintFunctor, Msg2Type>(file, function, line,
			message1, message2);
		streammutex.unlock();
	}

	/// Print a generic warning, error, etc to the streams, plus another
	/// message of arbitrary type. PrintFunctor is the functor to use for
	/// specialized printing. This does not lock the mutex; use only in
	/// emergencies!
	/// \param file     The source file name.
	/// \param function The function name.
	/// \param line     The source file line.
	/// \param message1 The message to print.
	/// \param message2 The second message to print.
	template<typename PrintFunctor, typename Msg2Type>
	void printDebug2_unguarded(const char *file, const char *function,
		int line, const char *message1, Msg2Type message2)
	{
		printStandard(file, function, line, message1);
		PrintFunctor::print(buffer, message2);
		flush();
	}

	/// Get a curl error string from an easy handle code.
	/// \param error The curl error code.
	/// \return The curl error string.
	static const char *curlGetError(CURLcode error) {
		return curl_easy_strerror(error); }
	/// Get a curl error string from a multi handle code.
	/// \param error The curl error code.
	/// \return The curl error string.
	static const char *curlGetError(CURLMcode error) {
		return curl_multi_strerror(error); }
	/// Get a curl error string from a shared handle code.
	/// \param error The curl error code.
	/// \return The curl error string.
	static const char *curlGetError(CURLSHcode error) {
		return curl_share_strerror(error); }

protected:
	/// Flush the buffer to the streams in the stream list.
	virtual void flush();

private:
	/// Print the warning/error/etc to the buffer.
	/// \param file     The source file name.
	/// \param function The function name.
	/// \param line     The source file line.
	/// \param message  The message to print.
	void printStandard(const char *file, const char *function, int line,
		const char *message);

public:
	// -------------------------- Printing functors ---------------------------

	/// Null functor to do no fancy printing.
	class Print1
	{
	public:
		/// Print extra terms.
		/// \param buffer A reference to the buffer.
		static void print(std::ostringstream &buffer) {}
	};

	/// Functor to try and get the string buffer to print a second message of
	/// an arbitrary type.
	class Print2
	{
	public:
		/// Print extra terms.
		/// \param buffer   A reference to the buffer.
		/// \param message2 The second message to print.
		template <typename Msg2Type>
		static void print(std::ostringstream &buffer, Msg2Type message2)
		{
			buffer << "    " << message2 << '\n';
		}
	};


	/// Functor to print a C warning, error, etc to the buffer. Gets the error
	/// information from the errno global.
	class PrintCErrno
	{
	public:
		/// Print extra terms.
		/// \param buffer A reference to the buffer.
		static void print(std::ostringstream &buffer);
	};

	/// Functor to print a C warning, error, etc to the buffer. Gets the error
	/// information from the code provided.
	class PrintCCode
	{
	public:
		/// Print extra terms.
		/// \param buffer A reference to the buffer.
		/// \param code   The error code.
		static void print(std::ostringstream &buffer, int code);
	};

#ifdef WIN32
	/// Functor to print a WIN32 warning, error, etc to the buffer. Gets the
	/// error information from GetLastError().
	class PrintWin32
	{
	public:
		/// Print extra terms.
		/// \param buffer A reference to the buffer.
		static void print(std::ostringstream &buffer);
	};
#endif

	/// Functor to print a libxml warning, error, etc to the buffer. Gets the
	/// error information from xmlGetLastError().
	class PrintXml
	{
	public:
		/// Print extra terms.
		/// \param buffer A reference to the buffer.
		static void print(std::ostringstream &buffer);
	};

	/// Functor to print a curl warning, error, etc to the buffer. Templated
	/// for the three error types.
	class PrintCurl
	{
	public:
		/// Print extra terms.
		/// \param buffer A reference to the buffer.
		/// \param code   The curl error code.
		template <typename CodeType>
		static void print(std::ostringstream &buffer, CodeType code)
		{
			buffer << "    " << code << ": " << curlGetError(code) << '\n';
		}
	};

	// -------------------------- Member variables ----------------------------

private:
	/// The stream mutex.
	Mutex streammutex;
	/// The list of output streams.
	std::list<std::ostream *> streams;
protected:
	/// The internal string buffer stream.
	std::ostringstream buffer;
};

/// Specialize the Print2 functor to print pointers in hex.
/// \param buffer   A reference to the buffer.
/// \param message2 A pointer cast to a void pointer.
template <>
void DebugConsole::Print2::print(std::ostringstream &buffer, void *message2);


/// A global pointer to the debug console instance. Must be initialized by
/// main().
extern DebugConsole *debugcon;


// ------------------------------ Helper macros -------------------------------

/// Print a warning, error, etc to the streams in the debug console.
/// \param message The message to print.
#define PDEBUG(message) \
	debugcon->printDebug1<DebugConsole::Print1> \
	(__FILE__, __FUNCTION__, __LINE__, message)

/// Print a warning, error, etc to the streams in the debug console, plus a
/// second message of any type that ostream understands.
/// \param message1 The first (string) message to print.
/// \param message2 The second message to print.
#define PDEBUG2(message1, message2) \
	debugcon->printDebug2<DebugConsole::Print2> \
	(__FILE__, __FUNCTION__, __LINE__, message1, message2)

#ifdef WIN32
/// Print a WIN32 warning, error, etc to the streams in the debug console. Gets
/// the error information from GetLastError().
/// \param message The message to print.
#define PDEBUG_WIN32(message) \
	debugcon->printDebug1<DebugConsole::PrintWin32> \
	(__FILE__, __FUNCTION__, __LINE__, message)
#endif

/// Print a C warning, error, etc to the streams in the debug console. Gets the
/// error information from the errno global.
/// \param message The message to print.
#define PDEBUG_C_ERRNO(message) \
	debugcon->printDebug1<DebugConsole::PrintCErrno> \
	(__FILE__, __FUNCTION__, __LINE__, message)

/// Print a C warning, error, etc to the streams in the debug console. Gets the
/// error information from the code provided.
/// \param message The message to print.
/// \param code    The CRT error code.
#define PDEBUG_C_CODE(message, code) \
	debugcon->printDebug2<DebugConsole::PrintCCode> \
	(__FILE__, __FUNCTION__, __LINE__, message, code)

/// Print a curl warning, error, etc to the streams in the debug console.
/// \param message The message to print.
/// \param code    The curl error code.
#define PDEBUG_CURL(message, code) \
	debugcon->printDebug2<DebugConsole::PrintCurl> \
	(__FILE__, __FUNCTION__, __LINE__, message, code)

/// Print a libxml warning, error, etc to the streams in the debug console.
/// Gets the error information from xmlGetLastError().
/// \param message The message to print.
#define PDEBUG_XML(message) \
	debugcon->printDebug1<DebugConsole::PrintXml> \
	(__FILE__, __FUNCTION__, __LINE__, message)



/// Print a warning, error, etc to the streams in the debug console.
/// Does not lock the mutex. Only use in emergencies!
/// \param message The message to print.
#define UNGUARDED_PDEBUG(message) \
	debugcon->printDebug1_unguarded<DebugConsole::Print1> \
	(__FILE__, __FUNCTION__, __LINE__, message)

/// Print a warning, error, etc to the streams in the debug console, plus a
/// second message of any type that ostream understands.
/// Does not lock the mutex. Only use in emergencies!
/// \param message1 The first (string) message to print.
/// \param message2 The second message to print.
#define UNGUARDED_PDEBUG2(message1, message2) \
	debugcon->printDebug2_unguarded<DebugConsole::Print2> \
	(__FILE__, __FUNCTION__, __LINE__, message1, message2)

#ifdef WIN32
/// Print a WIN32 warning, error, etc to the streams in the debug console. Gets
/// the error information from GetLastError().
/// Does not lock the mutex. Only use in emergencies!
/// \param message The message to print.
#define UNGUARDED_PDEBUG_WIN32(message) \
	debugcon->printDebug1_unguarded<DebugConsole::PrintWin32> \
	(__FILE__, __FUNCTION__, __LINE__, message)
#endif

/// Print a C warning, error, etc to the streams in the debug console. Gets the
/// error information from the errno global.
/// Does not lock the mutex. Only use in emergencies!
/// \param message The message to print.
#define UNGUARDED_PDEBUG_C_ERRNO(message) \
	debugcon->printDebug1_unguarded<DebugConsole::PrintCErrno> \
	(__FILE__, __FUNCTION__, __LINE__, message)

/// Print a C warning, error, etc to the streams in the debug console. Gets the
/// error information from the code provided.
/// Does not lock the mutex. Only use in emergencies!
/// \param message The message to print.
/// \param code    The CRT error code.
#define UNGUARDED_PDEBUG_C_CODE(message, code) \
	debugcon->printDebug2_unguarded<DebugConsole::PrintCCode> \
	(__FILE__, __FUNCTION__, __LINE__, message, code)

/// Print a curl warning, error, etc to the streams in the debug console.
/// Does not lock the mutex. Only use in emergencies!
/// \param message The message to print.
/// \param code    The curl error code.
#define UNGUARDED_PDEBUG_CURL(message, code) \
	debugcon->printDebug2_unguarded<DebugConsole::PrintCurl> \
	(__FILE__, __FUNCTION__, __LINE__, message, code)

/// Print a libxml warning, error, etc to the streams in the debug console.
/// Gets the error information from xmlGetLastError().
/// Does not lock the mutex. Only use in emergencies!
/// \param message The message to print.
#define UNGUARDED_PDEBUG_XML(message) \
	debugcon->printDebug1_unguarded<DebugConsole::PrintXml> \
	(__FILE__, __FUNCTION__, __LINE__, message)




#else // #ifdef USING_DEBUGCONSOLE

// If the debug console is disabled, do nothing with the macros.

#define PDEBUG(message)
#define PDEBUG2(message1, message2)
#define PDEBUG_WIN32(message)
#define PDEBUG_C_ERRNO(message)
#define PDEBUG_C_CODE(message, code) code
#define PDEBUG_CURL(message, code) code
#define PDEBUG_XML(message)

#define UNGUARDED_PDEBUG(message)
#define UNGUARDED_PDEBUG2(message1, message2)
#define UNGUARDED_PDEBUG_WIN32(message)
#define UNGUARDED_PDEBUG_C_ERRNO(message)
#define UNGUARDED_PDEBUG_C_CODE(message, code) code
#define UNGUARDED_PDEBUG_CURL(message, code) code
#define UNGUARDED_PDEBUG_XML(message)

#endif // #ifdef USING_DEBUGCONSOLE
